﻿#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include "securec.h"
#include "ohos_bt_def.h"
#include "ohos_bt_gap.h"
#include "ohos_bt_gatt.h"
#include "ohos_bt_gatt_server.h"

#define DBG_FILE_NAME (__builtin_strrchr("/" __FILE__, '/') + 1)

#define DBG_PRINT(format, arg...)                                                   \
    do {                                                                            \
        printf("[demo][INFO ]:[%-20s:%4d]" format, DBG_FILE_NAME, __LINE__, ##arg); \
        printf("\n");                                                               \
    } while (0)

#define DBG_PRINT_ERROR(format, arg...)                                             \
    do {                                                                            \
        printf("[demo][ERROR]:[%-20s:%4d]" format, DBG_FILE_NAME, __LINE__, ##arg); \
        printf("\n");                                                               \
    } while (0)

#ifndef OK
#define OK 0
#endif

#ifndef ERROR
#define ERROR (-1)
#endif

// 终端支持的配网能力偏移位
typedef enum {
    OH_CFGNET_BLE = 0,
    OH_CFGNET_SLE = 1,
    OH_CFGNET_COAP = 2,
    OH_CFGNET_SOFTAP = 3,
} OhCfgnetCapacityOffsetBitE;

// OH BLE广播持续时间的最大时间,单位秒
#define MAX_OH_BLE_ADV_DURATION (20 * 60)

// OH BLE广播持续时间,单位10毫秒
#define OH_BLE_ADV_DURATION (15 * 60 * 100)

// OH BLE广播发送间隔
#define OH_BLE_ADV_MIN_INTERVAL (0x30)
#define OH_BLE_ADV_MAX_INTERVAL (0x60)

// OH BLE服务UUID
#define OH_BLE_SERVICE_UUID 0xFDEE

// OH 广播数据最大长度
#define MAX_OH_ADV_DATA_LEN 31

// 厂商自定义的产品名称最大长度
#define MAX_FAC_PRODUCT_NAME 10

// 靠近发现广播类型
#define BLE_NEARBY_DISCOVERY_ADS_AD_TYPE (0x16)

// 靠近发现广播AD Structure中SN后缀长度
#define BLE_NEARBY_DISCOVERY_ADS_SN_SUFFIX_LEN 2

// 设备名称广播类型
#define BLE_LOCAL_NAME_ADS_AD_TYPE (0x09)

// 设备名称广播AD Structure中SN后缀长度
#define BLE_LOCAL_NAME_ADS_SN_SUFFIX_LEN 4

// 单个字节长度
#define OH_BYTE_BITS 8

// ble 广播数据接口
typedef struct {
    unsigned char advLength;
    unsigned char *advData;
    unsigned char scanRspLength;
    unsigned char *scanRspData;
    // 实际发射功率
    unsigned char actualTxPower;
    // BLE广播发送最小间隔,实际间隔=[N*0.625ms],0表示由设备设置默认值
    unsigned short minInterval;
    // BLE广播发送最大间隔,实际间隔=[N*0.625ms],0表示由设备设置默认值
    unsigned short maxInterval;
    // BLE广播发送持续时间,实际时间=[N*10ms],0表示永久发送
    unsigned short duration;
} AdlBleAdvDataT;

// ble广播动态参数接口
typedef struct {
    char *prodID;
    char *sn;
    char *prodName;
    unsigned char actualTxPower;
    unsigned char cfgnetCapacity;
} AdlBleAdvParT;

// 广播动态参数
static AdlBleAdvParT g_adlBlePar = {
    .prodID = "XXXXX",                 // OH产品ID，须要申请
    .sn = "8888000521323231110000020", // 设备sn
    .prodName = "andlinkB",            // 设备自定义产品名称，最大10B
    .actualTxPower = -22,              // 设备实际发射功率
    .cfgnetCapacity = (1 << OH_CFGNET_BLE) | (1 << OH_CFGNET_SOFTAP)
};

// 进程是否退出标记
static int g_quit = 0;

// 广播句柄
static int32_t g_advId = -1;

// 获取产品ID
static char *AdlGetOHProdId()
{
    return g_adlBlePar.prodID;
}

// 获取设备SN
static char *AdlGetDeviceSn()
{
    return g_adlBlePar.sn;
}

// 获取设备自定义产品名称prodName
static char *AdlGetDeviceLocalName()
{
    return g_adlBlePar.prodName;
}

// 获取设备实际发射功率
static uint8_t AdlGetDeviceActualTxPower()
{
    return g_adlBlePar.actualTxPower;
}

// 获取设备支持的配网能力
static uint8_t AdlGetDeviceCfgnetCapacity()
{
    return g_adlBlePar.cfgnetCapacity;
}

/************************************************************************
Description: 封装IoT connect SDK的蓝牙广播发送接口
Input:  int32 *advId, AdlBleAdvDataT *adv
Output: None
Return: 成功:0, 失败:-1
************************************************************************/
int32_t OhStartBleAdvData(int32_t *advId, AdlBleAdvDataT *adv)
{
    int32_t ret = 0;
    int32_t id = -1;

    // 1. 配置广播参数
    BleAdvParams advParam = { 0 };
    advParam.minInterval = adv->minInterval; // 最小广播间隔
    advParam.maxInterval = adv->maxInterval; // 最大广播间隔
    advParam.advType = OHOS_BLE_ADV_IND;     // 设置广播类型为可连接广播
    advParam.ownAddrType = OHOS_BLE_RANDOM_DEVICE_ADDRESS;
    advParam.peerAddrType = OHOS_BLE_RANDOM_DEVICE_ADDRESS;
    // advParam.peerAddr.addr;
    advParam.channelMap = 0x07; // 使用所有三个广播信道
    advParam.advFilterPolicy = OHOS_BLE_ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY;
    advParam.txPower = adv->actualTxPower;
    advParam.duration = adv->duration;

    // 2. 配置广播数据
    StartAdvRawData advData = { 0 };
    advData.advData = adv->advData;
    advData.advDataLen = adv->advLength;
    advData.rspData = adv->scanRspData;
    advData.rspDataLen = adv->scanRspLength;

    // 3. 启动广播
    ret = BleStartAdvEx(&id, advData, advParam);
    if (ret != OHOS_BT_STATUS_SUCCESS) {
        DBG_PRINT_ERROR("Failed to BleStartAdvEx, ret=%d", ret);
        return ret;
    }
    *advId = id;
    DBG_PRINT("call BleStartAdvEx OK,advDataLen=%u,rspDataLen=%u", advData.advDataLen, advData.rspDataLen);

    return OK;
}

/************************************************************************
Description: 封装IoT connect SDK的蓝牙广播关闭接口
Input:  int32_t advId
Output: None
Return: 成功:0, 失败:-1
************************************************************************/
int32_t OhStopBleAdvData(int32_t advId)
{
    int32_t ret = 0;
    ret = BleStopAdv(advId);
    if (OK != ret) {
        DBG_PRINT_ERROR("Failed to BleStopAdv,ret =%d", ret);
        return ERROR;
    }
    DBG_PRINT("call BleStopAdv OK");

    return OK;
}

/************************************************************************
Description: 设置ble发现模式 AD Structure
Input:  uint8_t *data, uint8_t maxLen
Output: uint8_t *data
Return: 内容长度
************************************************************************/
static uint8_t AdlSetBleDiscoverMode(uint8_t *data, uint8_t maxLen)
{
    uint8_t dataLen = 0;
    // 1.发现模式
    // GAP_ADTYPE_FLAGS
    // LE Limited Discoverable Mode+GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED
    uint8_t mode[3] = { 0x02, 0x01, 0x06 };
    if (memcpy_s(&data[dataLen], maxLen, mode, sizeof(mode)) != 0) {
        return 0;
    }
    dataLen += (uint8_t)sizeof(mode);
    return dataLen;
}

/************************************************************************
Description: 设置ble靠近发现 AD Structure
Input:  uint8_t *data, uint8_t maxLen, AdlBleAdvParT *dynPar
Output: uint8_t *data
Return: 内容长度
************************************************************************/
static uint8_t AdlSetOHBleNearbyDiscovery(uint8_t *data, uint8_t maxLen, AdlBleAdvParT *dynPar)
{
    char *prodID = dynPar->prodID;
    char *sn = dynPar->sn;
    uint8_t actualTxPower = dynPar->actualTxPower;

    if (NULL == prodID) {
        DBG_PRINT_ERROR("input prodID is NULL");
        return 0;
    }

    // 2.靠近发现
    uint8_t dataLen = 0;
    uint8_t fieldLen = 0;

    data[dataLen++] = 0;                                // 后续数据长度
    data[dataLen++] = BLE_NEARBY_DISCOVERY_ADS_AD_TYPE; // 靠近发现广播类型

    // 2.1 UUID(2)
    data[dataLen++] = OH_BLE_SERVICE_UUID & 0xFF;
    data[dataLen++] = (OH_BLE_SERVICE_UUID >> OH_BYTE_BITS) & 0xFF;

    // 2.2 Version(1)
    data[dataLen++] = 0x01;

    // 2.3 Business(2)
    data[dataLen++] = 0x17; // business-OH设备
    data[dataLen++] = 0x07; // business-拉起半模态

    // 2.4 SubProdID(1+1)
    data[dataLen++] = 0x04; // SubProdID-Type
    data[dataLen++] = 0x00; // SubProdID-Value

    // 2.5 AdvPowerTRP(1+1)
    data[dataLen++] = 0x11;          // AdvPowerTRP-Type
    data[dataLen++] = actualTxPower; // AdvPowerTRP-Value(设备广播的实际发射功率) = TxPower-OTA

    // 2.6 ProdID(1+5)
    data[dataLen++] = 0x15; // ProdID-Type
    if (memcpy_s(&data[dataLen], maxLen - dataLen, prodID, strlen(prodID)) != 0) {
        return 0;
    }
    dataLen += strlen(prodID);

    // 2.7 分隔符(1)
    data[dataLen++] = 0xFF;

    // 2.8 ProtocolD(1+1+1)
    data[dataLen++] = 0x17; // ProtocolD-T
    data[dataLen++] = 0x01; // ProtocolD-L
    data[dataLen++] = 0x01; // ProtocolD-V,弹窗

    // 2.9 SN(1+1+2)  取sn后2位
    data[dataLen++] = 0x14; // SN-T
    data[dataLen++] = 0x02; // SN-L
    fieldLen = BLE_NEARBY_DISCOVERY_ADS_SN_SUFFIX_LEN;
    if (memcpy_s(&data[dataLen], maxLen - dataLen, sn + strlen(sn) - fieldLen, fieldLen) != 0) {
        DBG_PRINT_ERROR("encode sn suffix error");
        return 0;
    }
    dataLen += fieldLen;
    data[0] = dataLen - 1;
    return dataLen;
}

/************************************************************************
Description: 设置ble local name的AD Structure
Input:  uint8_t *data, uint8_t maxLen, AdlBleAdvParT *dynPar
Output: uint8_t *data
Return: 内容长度
Others: AD Structure
************************************************************************/
static uint8_t AdlSetOHBleLocalName(uint8_t *data, uint8_t maxLen, AdlBleAdvParT *dynPar)
{
    char *prodID = dynPar->prodID;
    char *sn = dynPar->sn;
    char *prodName = dynPar->prodName;

    uint8_t dataLen = 0;
    uint8_t fieldLen = 0;

    data[dataLen++] = 0x0;
    data[dataLen++] = BLE_LOCAL_NAME_ADS_AD_TYPE; // 设备名称广播类型

    // 1.1 前缀(3)
    data[dataLen++] = 'O';
    data[dataLen++] = 'h';
    data[dataLen++] = '-';

    // 1.2 prodName(<=10)
    uint32_t len = strlen(prodName);
    if (0 == len || len > MAX_FAC_PRODUCT_NAME) {
        DBG_PRINT_ERROR("prodName is too long");
        return 0;
    }
    if (memcpy_s(&data[dataLen], maxLen - dataLen, prodName, len) != 0) {
        DBG_PRINT_ERROR("input par space is small");
        return 0;
    }
    dataLen += len;

    // 1.2 分隔符(2)
    data[dataLen++] = '-';
    data[dataLen++] = '1';

    // 1.3 ProdID(5)
    if (memcpy_s(&data[dataLen], maxLen - dataLen, prodID, strlen(prodID)) != 0) {
        DBG_PRINT_ERROR("input par space is small");
        return 0;
    }
    dataLen += strlen(prodID);

    // 1.4 SN后4位
    fieldLen = BLE_LOCAL_NAME_ADS_SN_SUFFIX_LEN;
    if (memcpy_s(&data[dataLen], maxLen - dataLen, sn + strlen(sn) - fieldLen, fieldLen) != 0) {
        DBG_PRINT_ERROR("input par space is small");
        return 0;
    }
    dataLen += fieldLen;

    // 1.5 终端支持的配网能力(1)
    uint8_t cfgnetCapacity = 0x60 | AdlGetDeviceCfgnetCapacity();
    data[dataLen++] = cfgnetCapacity;

    // 设置AD Type和AD Data总长度
    data[0] = dataLen - 1;
    return dataLen;
}

/************************************************************************
Description: 设置ble广播的内容
Input:  uint8_t *data, uint8_t maxLen
Output: uint8_t *data
Return: ble广播的内容长度
************************************************************************/
static uint8_t AdlOHSetAdvData(uint8_t *data, uint8_t maxLen)
{
    uint8_t dataLen = 0;
    if (NULL == data || maxLen < MAX_OH_ADV_DATA_LEN) {
        DBG_PRINT_ERROR("func par error");
        return dataLen;
    }
    AdlBleAdvParT advDynPar;
    memset_s(&advDynPar, sizeof(advDynPar), 0, sizeof(advDynPar));

    advDynPar.prodID = AdlGetOHProdId();
    advDynPar.sn = AdlGetDeviceSn();
    advDynPar.actualTxPower = (uint8_t)AdlGetDeviceActualTxPower(); // TxPower - OTA,转换为补码
    if (NULL == advDynPar.prodID || 0 == strlen(advDynPar.prodID)) {
        DBG_PRINT_ERROR("OH prodID is invalid");
        return dataLen;
    }
    if (0 == advDynPar.actualTxPower) {
        DBG_PRINT_ERROR("OH actualTxPower is invalid");
        return dataLen;
    }

    dataLen += AdlSetBleDiscoverMode(data, maxLen);
    dataLen += AdlSetOHBleNearbyDiscovery(&data[dataLen], maxLen - dataLen, &advDynPar);

    DBG_PRINT("AdlOHSetAdvData[%u]", dataLen);
    return dataLen;
}

/************************************************************************
Description: 设置ble扫描响应
Input:  uint8_t *data, uint8_t maxLen
Output: uint8_t *data
Return: ble 扫描响应的内容长度
************************************************************************/
static uint8_t AdlOHSetScanRspData(uint8_t *data, uint8_t maxLen)
{
    uint8_t dataLen = 0;
    if (NULL == data || maxLen < MAX_OH_ADV_DATA_LEN) {
        DBG_PRINT_ERROR("func par error");
        return dataLen;
    }

    AdlBleAdvParT advDynPar;
    memset_s(&advDynPar, sizeof(advDynPar), 0, sizeof(advDynPar));

    advDynPar.prodID = AdlGetOHProdId();
    advDynPar.sn = AdlGetDeviceSn();
    advDynPar.prodName = AdlGetDeviceLocalName();

    if (NULL == advDynPar.prodID || 0 == strlen(advDynPar.prodID)) {
        DBG_PRINT_ERROR("OH prodID is invalid");
        return dataLen;
    }

    if (NULL == advDynPar.sn || 0 == strlen(advDynPar.sn)) {
        DBG_PRINT_ERROR("OH sn is invalid");
        return dataLen;
    }
    if (0 == strlen(advDynPar.prodName)) {
        DBG_PRINT_ERROR("OH prodName is invalid");
        return dataLen;
    }

    dataLen += AdlSetOHBleLocalName(data, maxLen, &advDynPar);

    DBG_PRINT("AdlOHSetScanRspData[%u]", dataLen);
    return dataLen;
}

/************************************************************************
Description: 开始发送ble广播
Input: int32_t *advId 广播id
Output: int32_t *advId
Return: 成功:0, 失败:-1
************************************************************************/
int32_t AdlStartBleAdvData(int32_t *advId)
{
    uint8_t advData[MAX_OH_ADV_DATA_LEN] = { 0 };
    uint8_t scanRspData[MAX_OH_ADV_DATA_LEN] = { 0 };
    AdlBleAdvDataT adv;
    memset_s(&adv, sizeof(adv), 0, sizeof(adv));
    adv.advData = advData;
    adv.scanRspData = scanRspData;

    adv.minInterval = OH_BLE_ADV_MIN_INTERVAL;
    adv.maxInterval = OH_BLE_ADV_MAX_INTERVAL;
    adv.duration = (uint16_t)OH_BLE_ADV_DURATION;
    adv.advLength = AdlOHSetAdvData(advData, sizeof(advData));
    adv.scanRspLength = AdlOHSetScanRspData(scanRspData, sizeof(scanRspData));

    if (0 == adv.advLength || 0 == adv.scanRspLength) {
        return ERROR;
    }

    if (OK != OhStartBleAdvData(advId, &adv)) {
        return ERROR;
    }
    DBG_PRINT("[enter]adl start ble adv data,advId =0x%.2x", *advId);
    return OK;
}

/************************************************************************
Description: 停止发送ble广播
Input: int32_t advId 广播id
Output: None
Return: 成功:0, 失败:-1
************************************************************************/
int32_t AdlStopBleAdvData(int32_t advId)
{
    DBG_PRINT("adl stop ble adv data,advId =0x%.2x", advId);
    return OhStopBleAdvData(advId);
}

int32_t main(void)
{
    DBG_PRINT("[enter] OH nearby discovery popup.");
    int32_t ret = 0;

    // 1.初始化BLE栈
    ret = InitBtStack();
    if (ret != OHOS_BT_STATUS_SUCCESS) {
        DBG_PRINT_ERROR("Failed to init stack, ret=%d", ret);
        return ERROR;
    }
    ret = EnableBtStack();
    if (ret != OHOS_BT_STATUS_SUCCESS) {
        DBG_PRINT_ERROR("Failed to enable stack, ret=%d", ret);
        DisableBtStack();
        return ERROR;
    }

    // 3.发送靠近发现广播
    ret = AdlStartBleAdvData(&g_advId);
    if (ret != OK) {
        DBG_PRINT_ERROR("Failed to AdlStartBleAdvData, ret=%d", ret);
        DisableBtStack();
        return ERROR;
    }

    int32_t cycCnt = 0;
    while (!g_quit) {
        sleep(1);
        if (++cycCnt > MAX_OH_BLE_ADV_DURATION) {
            break;
        }
    }

    // 4.停止靠近发现广播
    AdlStopBleAdvData(g_advId);

    // 5.销毁BLE协议栈
    ret = DisableBtStack();
    if (ret != OHOS_BT_STATUS_SUCCESS) {
        DBG_PRINT_ERROR("disable bt stack,ret=%d", ret);
        return ERROR;
    }
    DBG_PRINT("disable bt stack OK");
    return OK;
}